home *** CD-ROM | disk | FTP | other *** search
-
- program TXREF;
-
- {$V-}
- {$R+}
-
- { Program TXREF - Produce a Listing and Cross Reference for a Turbo Pascal
- source file.
-
- You must have Turbo Toolbox from Borland International, Inc. in order to
- compile this program.
-
- As written, this program assumes that you have an Epson FX-80 printer. It
- may work on other printers if they are compatible enough.
-
- By Michael Quinlan
- Version 1.0.0
- 12/1/84
-
- Known bugs:
-
- 1. This program doesn't correctly handle certain types of constants;
- the 'E' in a floating point constant will be considered a name as
- will some hex constants. The procedure CopyTillAlpha needs to be
- re-written to handle these things better.
-
- 2. Numeric labels are not included in the cross reference.
-
- 3. Names longer than 79 bytes may mess up the page alignment while
- printing the cross reference.
-
- }
-
- const
- LinesPerPage = 60;
-
- Type
- Str = String[127];
- XrefRec = record
- Name : Str;
- Page : Integer;
- Line : Integer;
- end;
-
- var
- XrefVar : XrefRec;
- NumOnLine : Integer;
- CurLine : Integer;
- CurPage : Integer;
- SortResult : Integer;
- InFileName : Str;
- InFile : Text;
- Line : Str;
- CurPosn : Integer;
- CommentStatus : (NoComment, CurlyBracket, ParenStar);
- InsideString : Boolean;
-
- const
- NumReservedWords = 44;
- BiggestReservedWord = 9;
- ReservedWordList : array [1..NumReservedWords] of String[BiggestReservedWord]
- = (
- 'ABSOLUTE', 'AND' , 'ARRAY' , 'BEGIN', 'CASE' , 'CONST' , 'DIV',
- 'DO' , 'DOWNTO' , 'ELSE' , 'END' , 'EXTERNAL', 'FILE' , 'FOR',
- 'FORWARD' , 'FUNCTION', 'GOTO' , 'IF' , 'IN' , 'INLINE', 'LABEL',
- 'MOD' , 'NIL' , 'NOT' , 'OF' , 'OR' , 'PACKED', 'PROCEDURE',
- 'PROGRAM' , 'RECORD' , 'REPEAT', 'SET' , 'SHL' , 'SHR' , 'STRING',
- 'THEN' , 'TO' , 'TYPE' , 'UNTIL', 'VAR' , 'WHILE' , 'WITH',
- 'XOR' , 'OVERLAY');
-
- var
- ReservedWordHashTable : array [1..NumReservedWords] of
- record
- WordPtr : Integer;
- NextPtr : Integer
- end;
-
- {$IC:SORT.BOX} { Include the sort routines from Turbo ToolBox }
-
- {=======================================================================}
- { Printer Routines }
- {=======================================================================}
-
- procedure Printer_Init;
- { Init the printer to 132 column mode }
- begin
- Write(Lst, #15)
- end;
-
- procedure Printer_Reset;
- { reset printer back to 80 column mode }
- begin
- Write(Lst, #18) { turn compressed mode off }
- end;
-
- procedure Printer_Underscore;
- { Turn on underlines }
- begin
- Write(Lst, #27'-1') { turn on underlines }
- end;
-
- procedure Printer_NoUnderscore;
- { Turn off underlines }
- begin
- Write(Lst, #27'-0') { turn off underlines }
- end;
-
- procedure Printer_Eject;
- { Eject to a new page }
- begin
- Write(Lst, #12)
- end;
-
- {======================================================================}
- { Procedures for handling the hash table; this is used to speed up }
- { checking for reserved words. }
- {======================================================================}
-
- function ReservedWordHash(var w : Str) : Integer;
- var
- c : char;
- h : integer;
- i : integer;
- n : integer;
- begin
- h := 0;
- n := 1;
- for i := 1 to (length(w) div 2) do
- begin
- h := h xor ((Ord(w[n]) shl 8) or Ord(w[n+1]));
- n := n + 2
- end;
- if n = length(w) then
- h := h xor Ord(w[n]);
- ReservedWordHash := ((h and $7FFF) mod NumReservedWords) + 1
- end;
-
- procedure SetUpReservedWordHashTable;
- var
- h : integer;
- i : integer;
- NewH : integer;
- MinProbes, MaxProbes, NumProbes, TotProbes : integer; { for debugging only }
- AvgProbes : Real; { for debugging only }
-
- function FindFreeEntry(h : integer) : integer;
- begin
- repeat
- if h >= NumReservedWords then h := 1
- else h := h + 1
- until ReservedWordHashTable[h].WordPtr = 0;
- FindFreeEntry := h
- end;
-
- begin
- for i := 1 to NumReservedWords do
- begin
- ReservedWordHashTable[i].WordPtr := 0;
- ReservedWordHashTable[i].NextPtr := 0
- end;
- for i := 1 to NumReservedWords do
- begin
- h := ReservedWordHash(ReservedWordList[i]);
- if ReservedWordHashTable[h].WordPtr = 0 then
- ReservedWordHashTable[h].WordPtr := i
- else
- begin { handle collisions }
- { first find the end of the chain }
- while ReservedWordHashTable[h].NextPtr <> 0 do
- h := ReservedWordHashTable[h].NextPtr;
- { now attach the new entry onto the end of the chain }
- NewH := FindFreeEntry(h);
- ReservedWordHashTable[h].NextPtr := Newh;
- ReservedWordHashTable[NewH].WordPtr := i
- end
- end;
-
- { the following is for debugging only }
- (***********************************************************************
-
- D E B U G G I N G C O D E C O M M E N T E D O U T
-
- ***********************************************************************
-
- { calculate the min, max, and average number of probes required into the
- hash table }
- TotProbes := 0;
- MinProbes := MaxInt;
- MaxProbes := 0;
- for i := 1 to NumReservedWords do
- begin
- h := ReservedWordHash(ReservedWordList[i]);
- NumProbes := 1;
- while ReservedWordHashTable[h].WordPtr <> i do
- begin
- NumProbes := NumProbes + 1;
- h := ReservedWordHashTable[h].NextPtr
- end;
- TotProbes := TotProbes + NumProbes;
- if NumProbes > MaxProbes then MaxProbes := NumProbes;
- if NumProbes < MinProbes then MinProbes := NumProbes
- end;
- AvgProbes := TotProbes / NumReservedWords;
- writeln('RESERVED WORD HASH TABLE STATISTICS');
- writeln(' Max Probes = ', MaxProbes);
- writeln(' Min Probes = ', MinProbes);
- writeln(' Avg Probes = ', AvgProbes:8:2)
-
- *************************************************************************)
-
- end;
-
- {======================================================================}
- { Procedures to set up the input file. }
- {======================================================================}
-
- procedure UpStr(var s : Str);
- var
- i : integer;
- begin
- for i := 1 to length(s) do s[i] := UpCase(s[i])
- end;
-
- function GetParm : Str;
- var
- Parm : Str absolute CSeg:$80;
- begin
- GetParm := Parm
- end;
-
- function AskFileName : Str;
- var
- f : Str;
- begin
- Write('Name of file to cross reference: ');
- Readln(f);
- if f = '' then halt; { provide an exit for the user }
- AskFileName := f
- end;
-
- function OpenInFile : boolean;
- begin
- UpStr(InFileName); { convert file name to upper case }
- if Pos('.', InFileName) = 0 then InFileName := InFileName + '.PAS';
- Assign(InFile, InFileName);
- {$I-} Reset(InFile); {$I+}
- OpenInFile := (IOResult = 0)
- end;
-
- procedure GetInFile;
- begin
- { on entry, InFileName may already have the file name }
- if InFileName = '' then InFileName := AskFileName;
- while not OpenInFile do
- begin
- Writeln('Cannot open ', InFileName);
- InFileName := AskFileName
- end
- end;
-
- procedure NewPage;
- begin
- if CurPage = 0 then
- begin
- Writeln('Make sure printer is lined up at the top of the page and powered on.');
- Write('Press Enter when ready... ');
- readln;
- Printer_Init { set printer in 132 column mode }
- end
- else
- Printer_Eject;
- CurPage := CurPage + 1;
- CurLine := 1;
- Writeln(Lst, 'Page ', CurPage:5, 'Listing of ':60, InFileName);
- Writeln(Lst)
- end;
-
- procedure ReadLine;
- begin
- Readln(InFile, Line);
- if CurLine >= LinesPerPage then NewPage
- else CurLine := CurLine + 1;
- CurPosn := 1;
- InsideString := FALSE;
- Write(Lst, CurLine:2, ': ')
- end;
-
- {======================================================================}
- { Procedures to process the input file. }
- {======================================================================}
-
- procedure CopyTillAlpha;
- { copy chars from Line to the printer until the start of a name is found }
- begin
- while (CurPosn <= length(Line)) and
- (not (Line[CurPosn] in ['A'..'Z','a'..'z','_']) or InsideString or
- (CommentStatus <> NoComment)) do
- begin
- if CommentStatus = NoComment then
- begin
- if Line[CurPosn] = '''' then InsideString := not InsideString
- end;
- if not InsideString then
- case CommentStatus of
- NoComment : begin
- if Line[CurPosn] = '{' then CommentStatus := CurlyBracket
- else if CurPosn < length(Line) then
- begin
- if Copy(Line, CurPosn, 2) = '(*' then
- CommentStatus := ParenStar
- end
- end;
- CurlyBracket : if Line[CurPosn] = '}' then CommentStatus := NoComment;
- ParenStar : if CurPosn < length(Line) then
- begin
- if Copy(Line, CurPosn, 2) = '*)' then
- CommentStatus := NoComment
- end
- end; { Case }
- Write(Lst, Line[CurPosn]);
- CurPosn := CurPosn + 1
- end
- end;
-
- function Reserved(var w : Str) : boolean;
- var
- h : integer;
- r : (DontKnow, IsReserved, NotReserved);
- begin
- h := ReservedWordHash(w);
- r := DontKnow;
- repeat
- if w = ReservedWordList[ReservedWordHashTable[h].WordPtr] then
- r := IsReserved
- else if ReservedWordHashTable[h].NextPtr = 0 then
- r := NotReserved
- else h := ReservedWordHashTable[h].NextPtr
- until r <> DontKnow;
- Reserved := (r = IsReserved)
- end;
-
- procedure WriteReserved(var w : Str);
- begin
- Printer_Underscore; { turn on underscores }
- write(Lst, w);
- Printer_NoUnderscore { turn off underscores }
- end;
-
- procedure WriteWord(var Word, CapWord : Str);
- begin
- XrefVar.Name := CapWord;
- XrefVar.Page := CurPage;
- XrefVar.Line := CurLine;
- SortRelease(XrefVar);
- write(Lst, Word)
- end;
-
- procedure DoWord;
- var
- wstart : integer;
- Word : Str;
- CapWord : Str;
- begin
- wstart := CurPosn;
- repeat
- CurPosn := CurPosn + 1
- until (CurPosn > length(Line)) or not (Line[CurPosn] in ['A'..'Z','a'..'z','_','0'..'9']);
- Word := Copy(Line, wstart, CurPosn - wstart);
- CapWord := Word;
- UpStr(CapWord); { Upper case version of the word }
- if Reserved(CapWord) then
- WriteReserved(Word)
- else
- WriteWord(Word, CapWord)
- end;
-
- procedure Inp;
- begin
- GetInFile;
- CurLine := 1000; { force page break on first line }
- CurPage := 0;
- CommentStatus := NoComment;
- while not EOF(InFile) do
- begin
- ReadLine;
- while CurPosn <= length(Line) do
- begin
- CopyTillAlpha;
- if CurPosn <= length(Line) then DoWord
- end;
- Writeln(Lst)
- end
- end;
-
- {======================================================================}
- { Procedure called by TurboSort to order the cross reference entries }
- {======================================================================}
-
- function Less;
- var
- FirstRec : XrefRec absolute X;
- SecondRec : XrefRec absolute Y;
- begin
- if FirstRec.Name = SecondRec.Name then
- begin
- if FirstRec.Page = SecondRec.Page then
- Less := FirstRec.Line < SecondRec.Line
- else
- Less := FirstRec.Page < SecondRec.Page
- end
- else
- Less := FirstRec.Name < SecondRec.Name
- end;
-
- {======================================================================}
- { Procedures to print the sorted cross reference }
- {======================================================================}
-
- procedure Xref_NewPage;
- begin
- Printer_Eject;
- Writeln(Lst, 'C R O S S R E F E R E N C E':54);
- Writeln(Lst, 'Entries are PAGE:LINE':50);
- Writeln(Lst);
- CurLine := 0
- end;
-
- procedure Xref_NewLine;
- begin
- Writeln(Lst);
- if CurLine >= LinesPerPage then Xref_NewPage
- else CurLine := CurLine + 1;
- NumOnLine := 0
- end;
-
- procedure Xref_Write_Number(n, count : integer);
- { write "n" to Lst with "count" digits (add leading zeros) }
- var
- s : Str;
- i : integer;
- begin
- for i := count downto 1 do
- begin
- s[i] := Chr((n mod 10) + Ord('0'));
- n := n div 10
- end;
- s[0] := Chr(count); { set correct string length }
- write(Lst, s)
- end;
-
- procedure Xref_Write;
- begin
- if NumOnLine >= 8 then Xref_NewLine;
- if NumOnLine = 0 then Write(Lst, ' ');
- Write(Lst, ' ');
- Xref_Write_Number(XrefVar.Page, 5);
- Write(Lst, ':');
- Xref_Write_Number(XrefVar.Line, 2);
- NumOnLine := NumOnLine + 1
- end;
-
- procedure Xref_NewName;
- begin
- if (CurLine + 2) >= LinesPerPage then Xref_NewPage;
- Write(Lst, XrefVar.Name);
- Xref_NewLine
- end;
-
- procedure Outp;
- var
- CurName : Str;
- begin
- Printer_Reset; { put printer back into 80 column mode }
- Xref_NewPage;
- SortReturn(XrefVar);
- CurName := XrefVar.Name;
- Xref_NewName;
- Xref_Write;
- while not SortEOS do
- begin
- SortReturn(XrefVar);
- if CurName <> XrefVar.Name then
- begin
- Xref_NewLine;
- CurName := XrefVar.Name;
- Xref_NewName
- end;
- Xref_Write
- end;
- Writeln(Lst);
- Printer_Eject
- end;
-
- {======================================================================}
- { Main Program }
- {======================================================================}
-
- begin
- Write('Pascal Source Listing and Cross Reference Program V1.0.0');
- Writeln(' By Michael Quinlan');
- Writeln;
- SetUpReservedWordHashTable;
- InFileName := GetParm;
- while (length(InFileName)>0) and (InFileName[1] = ' ') do
- delete(InFileName, 1, 1);
- SortResult := TurboSort(SizeOf(XrefRec));
- writeln;
- case SortResult of
- 0 : Writeln('Program Completed OK');
- 3 : Writeln('Insufficient Memory for Sort');
- 8 : Writeln('Illegal Item Length for Sort (Program Logic Error)');
- 9 : Writeln('More Than ', MaxInt, ' Items to be Sorted');
- 10 : Writeln('Sort Error, Disk Error or Disk Full?');
- 11 : Writeln('Write Error During Sort, Bad Disk?');
- 12 : Writeln('File Creation Error During Sort')
- else
- Writeln('Unknown Error ', SortResult, ' From Sort')
- end; { Case }
- if SortResult <> 0 then
- Writeln('*** Sort Failed; Cross Reference Invalid or Incomplete')
- end.
-
-